1 package org.apache.lucene.util;
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20 import java.lang.reflect.Array;
21 import java.lang.reflect.Field;
22 import java.lang.reflect.Modifier;
23 import java.security.AccessController;
24 import java.security.PrivilegedAction;
25 import java.util.AbstractList;
26 import java.util.ArrayList;
27 import java.util.Collection;
28 import java.util.Collections;
29 import java.util.HashMap;
30 import java.util.IdentityHashMap;
31 import java.util.List;
32 import java.util.Map;
33 import java.util.Set;
34
35
36 public final class RamUsageTester {
37
38
39 public static class Accumulator {
40
41
42
43 public long accumulateObject(Object o, long shallowSize, Map<Field, Object> fieldValues, Collection<Object> queue) {
44 for (Object value : fieldValues.values()) {
45 queue.add(value);
46 }
47 return shallowSize;
48 }
49
50
51
52 public long accumulateArray(Object array, long shallowSize, List<Object> values, Collection<Object> queue) {
53 queue.addAll(values);
54 return shallowSize;
55 }
56
57 }
58
59
60
61
62
63
64
65
66
67
68 public static long sizeOf(Object obj, Accumulator accumulator) {
69 return measureObjectSize(obj, accumulator);
70 }
71
72
73 public static long sizeOf(Object obj) {
74 return sizeOf(obj, new Accumulator());
75 }
76
77
78
79
80
81
82 public static String humanSizeOf(Object object) {
83 return RamUsageEstimator.humanReadableUnits(sizeOf(object));
84 }
85
86
87
88
89
90
91
92 private static long measureObjectSize(Object root, Accumulator accumulator) {
93
94 final Set<Object> seen = Collections.newSetFromMap(new IdentityHashMap<Object, Boolean>());
95
96 final IdentityHashMap<Class<?>, ClassCache> classCache = new IdentityHashMap<>();
97
98 final ArrayList<Object> stack = new ArrayList<>();
99 stack.add(root);
100
101 long totalSize = 0;
102 while (!stack.isEmpty()) {
103 final Object ob = stack.remove(stack.size() - 1);
104
105 if (ob == null || seen.contains(ob)) {
106 continue;
107 }
108 seen.add(ob);
109
110 final Class<?> obClazz = ob.getClass();
111 assert obClazz != null : "jvm bug detected (Object.getClass() == null). please report this to your vendor";
112 if (obClazz.isArray()) {
113
114
115
116
117 final long shallowSize = RamUsageEstimator.shallowSizeOf(ob);
118 final int len = Array.getLength(ob);
119 final List<Object> values;
120 Class<?> componentClazz = obClazz.getComponentType();
121 if (componentClazz.isPrimitive()) {
122 values = Collections.emptyList();
123 } else {
124 values = new AbstractList<Object>() {
125
126 @Override
127 public Object get(int index) {
128 return Array.get(ob, index);
129 }
130
131 @Override
132 public int size() {
133 return len;
134 }
135
136 };
137 }
138 totalSize += accumulator.accumulateArray(ob, shallowSize, values, stack);
139 } else {
140
141
142
143
144 try {
145 ClassCache cachedInfo = classCache.get(obClazz);
146 if (cachedInfo == null) {
147 classCache.put(obClazz, cachedInfo = createCacheEntry(obClazz));
148 }
149
150 Map<Field, Object> fieldValues = new HashMap<>();
151 for (Field f : cachedInfo.referenceFields) {
152 fieldValues.put(f, f.get(ob));
153 }
154
155 totalSize += accumulator.accumulateObject(ob, cachedInfo.alignedShallowInstanceSize, fieldValues, stack);
156 } catch (IllegalAccessException e) {
157
158 throw new RuntimeException("Reflective field access failed?", e);
159 }
160 }
161 }
162
163
164 seen.clear();
165 stack.clear();
166 classCache.clear();
167
168 return totalSize;
169 }
170
171
172
173
174
175 private static final class ClassCache {
176 public final long alignedShallowInstanceSize;
177 public final Field[] referenceFields;
178
179 public ClassCache(long alignedShallowInstanceSize, Field[] referenceFields) {
180 this.alignedShallowInstanceSize = alignedShallowInstanceSize;
181 this.referenceFields = referenceFields;
182 }
183 }
184
185
186
187
188
189 private static ClassCache createCacheEntry(final Class<?> clazz) {
190 return AccessController.doPrivileged(new PrivilegedAction<ClassCache>() {
191 @Override
192 @SuppressForbidden(reason = "We need to access private fields of measured objects.")
193 public ClassCache run() {
194 ClassCache cachedInfo;
195 long shallowInstanceSize = RamUsageEstimator.NUM_BYTES_OBJECT_HEADER;
196 final ArrayList<Field> referenceFields = new ArrayList<>(32);
197 for (Class<?> c = clazz; c != null; c = c.getSuperclass()) {
198 if (c == Class.class) {
199
200 continue;
201 }
202 final Field[] fields = c.getDeclaredFields();
203 for (final Field f : fields) {
204 if (!Modifier.isStatic(f.getModifiers())) {
205 shallowInstanceSize = RamUsageEstimator.adjustForField(shallowInstanceSize, f);
206
207 if (!f.getType().isPrimitive()) {
208 f.setAccessible(true);
209 referenceFields.add(f);
210 }
211 }
212 }
213 }
214
215 cachedInfo = new ClassCache(
216 RamUsageEstimator.alignObjectSize(shallowInstanceSize),
217 referenceFields.toArray(new Field[referenceFields.size()]));
218 return cachedInfo;
219 }
220 });
221 }
222
223 }